//
// Copyright (c) 2009-2013 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty.  In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef NANOVG_GL_UTILS_H
#define NANOVG_GL_UTILS_H

struct NVGLUframebuffer {
	NVGcontext* ctx;
	GLuint fbo;
	GLuint rbo;
	GLuint texture;
	int image;
};
typedef struct NVGLUframebuffer NVGLUframebuffer;

// Helper function to create GL frame buffer to render to.
void nvgluBindFramebuffer(NVGLUframebuffer* fb);
NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags);
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb);

#endif // NANOVG_GL_UTILS_H

#ifdef NANOVG_GL_IMPLEMENTATION

#if defined(NANOVG_GL3) || defined(NANOVG_GLES2) || defined(NANOVG_GLES3)
// FBO is core in OpenGL 3>.
#	define NANOVG_FBO_VALID 1
#elif defined(NANOVG_GL2)
// On OS X including glext defines FBO on GL2 too.
#	ifdef __APPLE__
#		include <OpenGL/glext.h>
#		define NANOVG_FBO_VALID 1
#	endif
#endif

static GLint defaultFBO = -1;

NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags)
{
#ifdef NANOVG_FBO_VALID
    GLint defaultFBO;
    GLint defaultRBO;
    NVGLUframebuffer* fb = NULL;
    GLenum status = GL_FRAMEBUFFER_COMPLETE;

    KVS_GL_CALL( glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO) );
    KVS_GL_CALL( glGetIntegerv(GL_RENDERBUFFER_BINDING, &defaultRBO) );

    fb = (NVGLUframebuffer*)malloc(sizeof(NVGLUframebuffer));
    if (fb == NULL) goto error;
    memset(fb, 0, sizeof(NVGLUframebuffer));

    fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL);

#if defined NANOVG_GL2
    fb->texture = nvglImageHandleGL2(ctx, fb->image);
#elif defined NANOVG_GL3
    fb->texture = nvglImageHandleGL3(ctx, fb->image);
#elif defined NANOVG_GLES2
    fb->texture = nvglImageHandleGLES2(ctx, fb->image);
#elif defined NANOVG_GLES3
    fb->texture = nvglImageHandleGLES3(ctx, fb->image);
#endif

    fb->ctx = ctx;

    // frame buffer object
    KVS_GL_CALL( glGenFramebuffers(1, &fb->fbo) );
    KVS_GL_CALL( glBindFramebuffer(GL_FRAMEBUFFER, fb->fbo) );

    // render buffer object
    KVS_GL_CALL( glGenRenderbuffers(1, &fb->rbo) );
    KVS_GL_CALL( glBindRenderbuffer(GL_RENDERBUFFER, fb->rbo) );
    KVS_GL_CALL( glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, w, h) );

    // combine all
    KVS_GL_CALL( glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0) );
    KVS_GL_CALL( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo) );

    //if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) goto error;
//    GLenum status = GL_FRAMEBUFFER_COMPLETE;
    KVS_GL_CALL( status = glCheckFramebufferStatus(GL_FRAMEBUFFER) );
    if ( status != GL_FRAMEBUFFER_COMPLETE ) goto error;

    KVS_GL_CALL( glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO) );
    KVS_GL_CALL( glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO) );
    return fb;
error:
    KVS_GL_CALL( glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO) );
    KVS_GL_CALL( glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO) );
    nvgluDeleteFramebuffer(fb);
    return NULL;
#else
    NVG_NOTUSED(ctx);
    NVG_NOTUSED(w);
    NVG_NOTUSED(h);
    NVG_NOTUSED(imageFlags);
    return NULL;
#endif
}

void nvgluBindFramebuffer(NVGLUframebuffer* fb)
{
#ifdef NANOVG_FBO_VALID
    if (defaultFBO == -1) KVS_GL_CALL( glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO) );
	KVS_GL_CALL( glBindFramebuffer(GL_FRAMEBUFFER, fb != NULL ? fb->fbo : defaultFBO) );
#else
	NVG_NOTUSED(fb);
#endif
}

void nvgluDeleteFramebuffer(NVGLUframebuffer* fb)
{
#ifdef NANOVG_FBO_VALID
	if (fb == NULL) return;
	if (fb->fbo != 0)
            KVS_GL_CALL( glDeleteFramebuffers(1, &fb->fbo) );
	if (fb->rbo != 0)
            KVS_GL_CALL( glDeleteRenderbuffers(1, &fb->rbo) );
	if (fb->image >= 0)
		nvgDeleteImage(fb->ctx, fb->image);
	fb->ctx = NULL;
	fb->fbo = 0;
	fb->rbo = 0;
	fb->texture = 0;
	fb->image = -1;
	free(fb);
#else
	NVG_NOTUSED(fb);
#endif
}

#endif // NANOVG_GL_IMPLEMENTATION
